#include "stlcontourwidget.h"

STLContourWidget::STLContourWidget(std::shared_ptr<STLReader> stl_reader, const ContourInfo &contour_info, QWidget *parent)
{
    m_sp_stl_reader = stl_reader;
    m_plane_norm = contour_info.plane_norm;
    m_camera_dis = contour_info.distance;
    m_slice_lambda = contour_info.lambda;
    m_p_program = new QOpenGLShaderProgram(this);
}

STLContourWidget::~STLContourWidget()
{
    delete m_p_program;
}

void STLContourWidget::wheelEvent(QWheelEvent *event)
{
    updateContourIndexRangeInfo(m_contourHead, m_contourPointsCount, event->angleDelta().y() > 0.0f ? true : false);
    update();
}

void STLContourWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.3f, 0.5f, 1.0f, 1.0f);
    // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // 初始化摄像机
    m_camera.setPerspective(static_cast<float>(width()) / static_cast<float>(height()), 0.1f, 100.0f);
    m_camera.setMouseLockedStatus(true);
    QVector3D up(0.0f, 1.0f, 0.0f);
    if (qFuzzyCompare(qAbs(QVector3D::dotProduct(m_plane_norm.normalized(), up)), 1.0f))
        up = QVector3D(1.0f, 0.0f, 0.0f);
    m_camera.lookAt(m_camera_dis * m_plane_norm, QVector3D(0, 0, 0), up);

    // 编译和链接着色器
    if(!compileAndLinkShaders(m_p_program, ":/vertex_shaders/shaders_code/vs/vertex_shader_android.vs", ":/fragment_shaders/shaders_code/fs/fragment_shader_android.fs"))
    {
        qDebug() << "Failed to compile and link shaders.";
        return;
    }

    // 将模型拉到视野中心
    calAndSetModelInitPhase();

    // 提取轮廓数据
    QVector<Vertex> slice;
    QVector<Edge> contour_edges;
    QVector<Mesh> proper_meshes = h0(m_sp_stl_reader->getModelMeshes(), m_plane_norm, m_slice_lambda);
    h1(proper_meshes, m_plane_norm, m_slice_lambda, contour_edges);
    h2(contour_edges, m_contours);
    slice.clear();

    qDebug() << "当前切片有：" << m_contours.size() << "个轮廓";
    qDebug() << "当前切片深度：" << m_slice_lambda; // 0,1,0, -1.85023

    m_intersected_points_counts.clear();
    for(int k = 0; k < m_contours.size(); ++k)
    {
        slice.append(m_contours[k]);
        m_intersected_points_counts.append(m_contours[k].size());
    }

    qDebug() << "当前切片轮廓点共有： " <<slice.size();

    setContourShaderProgramDataFormation();

    setContourVertexData2Shader(slice);
}

void STLContourWidget::paintGL()
{
    glViewport(0, 0, this->width(), this->height());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲
    m_vao.bind();
    m_p_program->bind();

    m_p_program->setUniformValue("model", m_model);
    m_p_program->setUniformValue("view", m_camera.getViewMatrix());
    m_p_program->setUniformValue("projection", m_camera.getProjectionMatrix());

    // 设置法线矩阵
    QMatrix3x3 normModelMatrix = m_model.normalMatrix();
    m_p_program->setUniformValue("normModelMatrix", normModelMatrix);

    glDrawArrays(GL_POINTS, m_contourHead, m_contourPointsCount);
    m_p_program->release();
    m_vao.release();
}

void STLContourWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

bool STLContourWidget::loadShaderSource(const QString &filePath, QString &source)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "Failed to open shader file:" << filePath;
        return false;
    }

    QTextStream stream(&file);
    source = stream.readAll();
    file.close();
    return true;
}

bool STLContourWidget::compileAndLinkShaders(QOpenGLShaderProgram *p_program, const QString &vs_file_path, const QString &fs_file_path)
{
    if(p_program == nullptr)
        return false;

    QString vertexSource, fragmentSource;

    // 加载顶点着色器代码
    if (!loadShaderSource(vs_file_path, vertexSource))
    {
        return false;
    }

    // 加载片段着色器代码
    if (!loadShaderSource(fs_file_path, fragmentSource))
    {
        return false;
    }

    // 编译顶点着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource))
    {
        qDebug() << "Vertex shader compile error:" << p_program->log();
        return false;
    }

    // 编译片段着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource))
    {
        qDebug() << "Fragment shader compile error:" << p_program->log();
        return false;
    }

    // 链接着色器程序
    if (!p_program->link())
    {
        qDebug() << "Shader program link error:" << p_program->log();
        return false;
    }

    return true;
}

void STLContourWidget::setContourShaderProgramDataFormation()
{
    if(m_p_program == nullptr)
    {
        qDebug()<<"Shader promgram has not been initialized. Abort building shader program.";
        return;
    }
    m_vao.create();
    m_vao.bind();

    m_vbo.create();
    m_vbo.bind();

    m_p_program->bind();
    m_p_program->enableAttributeArray(0);
    m_p_program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, pos), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(1);
    m_p_program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, norm), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(2);
    m_p_program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, color), 4, sizeof(Vertex));

    m_vao.release();
    m_vbo.release();
    m_p_program->release();
}

void STLContourWidget::setContourVertexData2Shader(const QVector<Vertex> &contour_vertices)
{
    m_vbo.bind();
    assert(contour_vertices.size() * sizeof(Vertex) <= INT_MAX);
    m_vbo.allocate(contour_vertices.constData(), contour_vertices.size() * sizeof(Vertex)); // 如果顶点数量大于int的最大值，此处会异常
    m_vbo.release();
}

void STLContourWidget::updateContourIndexRangeInfo(GLint &contour_head, GLint &count, bool increaseOrDecrease)
{
    if(increaseOrDecrease && m_curShowContourIndice + 1 <= m_intersected_points_counts.size())
    {
        count = m_intersected_points_counts[m_curShowContourIndice];
        contour_head += m_curShowContourIndice == 0 ? -contour_head : m_intersected_points_counts[m_curShowContourIndice - 1];
        m_curShowContourIndice++;
    }
    else if(!increaseOrDecrease && m_curShowContourIndice > 0)
    {
        count = contour_head == 0 ? 0 : m_intersected_points_counts[m_curShowContourIndice - 2];
        contour_head -= contour_head == 0 ? contour_head : m_intersected_points_counts[m_curShowContourIndice - 2];
        m_curShowContourIndice--;
    }
}

void STLContourWidget::calAndSetModelInitPhase(bool isFlip)
{
    // 计算与设置STL模型初始相位
    m_centroid = m_model_attr_calculator.calModelCentroid(m_sp_stl_reader->getModelMeshes());
    m_model.translate(-m_centroid.x(), -m_centroid.y(), -m_centroid.z());
}
